<?php

use App\Models\Category;
use App\Models\Examination;
use App\Models\Question;
use App\Models\Redo;
use App\Models\Section;
use App\Models\Setting;
use App\Models\StudentResult;
use App\Models\TestList;
use App\Models\UserHasSection;
use Carbon\Carbon;
use Iidestiny\Flysystem\Oss\OssAdapter;
use Iidestiny\Flysystem\Oss\Plugins\SignatureConfig;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use League\Flysystem\Filesystem;

if (!function_exists('categoryGroup')) {
    /**
     * 方便分类控制器使用
     *
     * @param $request
     * @return array
     */
    function categoryGroup($request)
    {
        $status = $request['status'] ?? 1;

        if (!in_array($status, [0, 1])) {
            return [];
        }

        // 'subject', 'examination', 'paper', 'text-bank', 'menu'
        $group = $request['category_group'] ?? '';

        if (!in_array($group, array_values(Category::CATEGORY_GROUP))) {
            return [];
        }

        // cachePaperOn
        $key = 'cache' . camelCase($group) . ($status ? 'On' : 'Off');

        // 判断方法是否存在， 存在则调用 ，不存在返回空数组
        if (Redis::exists($key)) {
            // 生成树状结构
            return makeTree(unserialize(Redis::get($key)));
        }

        // 获取分组id
        $categoryGroupId = DB::table('category_groups')->where('flag', $group)->value('id');

        $data = Category::select('id', 'name', 'flag', 'pid', 'status', 'category_group_id', 'user_id')
            ->where('category_group_id', $categoryGroupId)
            ->where('status', $status)
            ->get()
            ->toArray();

        Redis::setex($key, 7200, serialize($data));

        return makeTree($data);
    }
}

if (!function_exists('cacheSubject')) {
    /**
     * 科目下的分类
     *
     * @param bool $status
     * @return array
     */
    function cacheSubject($status = true)
    {
        return categoryGroup(['category_group' => 'subject', 'status' => $status]);
    }
}

if (!function_exists('cacheExamination')) {
    /**
     * 考试下的分类
     *
     * @param bool $status
     * @return array
     */
    function cacheExamination($status = true)
    {
        return categoryGroup(['category_group' => 'examination', 'status' => $status]);
    }
}

if (!function_exists('cachePaper')) {
    /**
     * 试卷分类
     *
     * @param bool $status
     * @return array
     */
    function cachePaper($status = true)
    {
        return categoryGroup(['category_group' => 'paper', 'status' => $status]);
    }
}

if (!function_exists('cacheTextBank')) {
    /**
     * 题库分类
     *
     * @param bool $status
     * @return array
     */
    function cacheTextBank($status = true)
    {
        return categoryGroup(['category_group' => 'text-bank', 'status' => $status]);
    }
}

if (!function_exists('destroyCategoryCache')) {
    /**
     * 删除分类缓存
     * @param $group
     */
    function destroyCategoryCache($group)
    {
        Redis::del(['cache' . camelCase($group) . 'On', 'cache' . camelCase($group) . 'Off']);
    }
}

if (!function_exists('camelCase')) {
    /**
     * 首字母大写
     *
     * @param $string
     * @return string
     */
    function camelCase($string): string
    {
        $str = trim($string);

        if (strlen($str) == 0) {
            return $str;
        }

        //转换数组
        $arr = explode('-', $str);

        $end_s = '';

        foreach ($arr as $v) {
            $end_s .= ucfirst($v);
        }

        return $end_s;
    }
}

/**
 *
 * 获取自己以及自己子级部门下所有用户id
 *
 * 返回值: Array
 */

if (!function_exists('userIdAndAllChildrenId')) {
    function userIdAndAllChildrenId()
    {
        // 获取当前登录用户的所属的所有部门
        $thisSection = auth()->user()->section()->get();

        $data = [];

        if (Redis::exists(auth()->id() . ':section')) {
            $data = unserialize(Redis::get(auth()->id() . ':section'));
        } else {
            foreach ($thisSection as $item) {
                $path = $item['pivot']['path'];
                $res = DB::table('user_has_section')
                    ->select('user_id', 'path')
                    ->where('path', 'like', '%' . $path . '%')
                    ->where('path', '!=', $path)
                    ->get();

                array_push($data, $res);
            }

            Redis::command('set', [auth()->id() . ':section', serialize($data), 3600]);
        }

        $ids = [];
        foreach ($data as $v) {
            foreach ($v as $item) {
                array_push($ids, $item->user_id);
            }
        }
        // 将当前用户id插入查询到的id中
        array_push($ids, auth()->id());

        return $ids;
    }
}

// 所有部门的缓存
if (!function_exists('getCacheSection')) {
    function getCacheSection()
    {
        if (Redis::exists('CacheSection')) {
            return unserialize(Redis::get('CacheSection'));
        } else {
            $data = Section::query()
                ->select('id', 'name', 'pid', 'level', 'status')
                ->get()
                ->toArray();

            $data = makeTree($data);

            Redis::command('set', ['CacheSection', serialize($data), 3600]);

            return $data;
        }
    }
}


if (!function_exists('makeTree')) {
    /**
     *
     * 生成无限分类树
     *
     * @param $arr // 所有带有pid字段的数据
     * @return array // tree
     */
    function makeTree($arr)
    {
        $refer = array();
        $tree = array();
        foreach ($arr as $k => $v) {
            $refer[$v['id']] = &$arr[$k]; //创建主键的数组引用
        }
        foreach ($arr as $k => $v) {
            $pid = $v['pid'];  //获取当前分类的父级id
            if ($pid == 0) {
                $tree[] = &$arr[$k];  //顶级栏目
            } else {
                if (isset($refer[$pid])) {
                    $refer[$pid]['children'][] = &$arr[$k]; //如果存在父级栏目，则添加进父级栏目的子栏目数组中
                }
            }
        }
        return $tree;
    }
}

if (!function_exists('getMenuTree')) {
    /**
     * 递归无限级分类【先序遍历算】，获取任意节点下所有子孩子
     * @param $arrCat
     * @param int $pid
     * @param int $level 层级数
     * @return array $arrTree 排序后的数组
     */
    function getMenuTree($arrCat, $pid = 0, $level = 0)
    {
        static $arrTree = array(); //使用static代替global
        if (empty($arrCat)) return FALSE;
        $level++;
        foreach ($arrCat as $key => $value) {
            if ($value['pid'] == $pid) {
                $arrTree[] = $value;
                unset($arrCat[$key]); //注销当前节点数据，减少已无用的遍历
                getMenuTree($arrCat, $value['id'], $level);
            }
        }
        return $arrTree;
    }
}
//获取试卷的题
if (!function_exists('testQuestion')) {
    function testQuestion($testList, $type = '')
    {
        $sql = [];
        $fraction = [];

        if ($type == 'hasSuccess') {
            $select = ['id', 'title', 'content', 'answer', 'success_answer', 'fraction', 'analysis', 'type', 'category_id'];
        } else {
            $select = ['id', 'title', 'content', 'answer', 'fraction', 'analysis', 'type', 'category_id'];
        }

        // 构建查询语句
        if (!empty($testList->random)) {
            foreach ($testList->random as $key => $item) {
                foreach ($item as $value) {
                    // 获取各类型里分类对应的分数
                    $fraction[$key][$value['category_id']] = $value['fraction'];
                    if ($value['level'] != 0) {
                        $sql[] = "(select id from questions where `category_id` = {$value['category_id']} AND `level` ={$value['level']} AND `type` = '{$key}' order by `" . ['id', 'created_at', 'order', 'category_id', 'fraction', 'level', 'num', 'success_num'][rand(0, 7)] . "` " . ['desc', 'asc'][rand(0, 1)] . " limit 0, {$value['num']})";
                    } else {
                        $sql[] = "(select id from questions where `category_id` = {$value['category_id']} AND `type` = '{$key}' order by `" . ['id', 'created_at', 'order', 'category_id', 'fraction', 'level', 'num', 'success_num'][rand(0, 7)] . "` " . ['desc', 'asc'][rand(0, 1)] . " limit 0, {$value['num']})";
                    }
                }
            }

            // 合并sql语句
            $sql = implode(' union ', $sql);
            // 查询所有试卷中所有随机试题的id
            $ids = DB::select($sql);

            // 获取id数组
            $ids = collect($ids)->pluck('id');

            // 查询所有的随机题目
            $randoms = Question::query()
                ->select($select)
                ->whereIn('id', $ids)
                ->get()
                ->toArray();
        }

        if (!empty($testList->fixed)) {
            // 查询所有的固定题目
            $fixeds = Question::query()
                ->select($select)
                ->whereIn('id', $testList->fixed)
                ->get()
                ->toArray();
        }

        // 获取试卷的试题排序条件
        $orders = explode(',', $testList->order);

        $data = [];
        // 对所有试题类型进行排序
        foreach ($orders as $order) {
            // 排序随机题目
            if (!empty($randoms)) {
                foreach ($randoms as $random) {
                    $random['fraction'] = $fraction[$order][$random['category_id']] ?? $random['fraction'];
                    if ($random['type'] == $order) {
                        array_push($data, $random);
                    }
                }
            }

            // 排序固定题目
            if (!empty($fixeds)) {
                foreach ($fixeds as $fixed) {
                    $fixed['fraction'] = $testList->fixed[$fixed['id']] ?? $fixed['fraction'];
                    if ($fixed['type'] == $order) {
                        array_push($data, $fixed);
                    }
                }
            }
        }

        return $data;
    }
}


if (!function_exists('createStudentResult')) {
    function createStudentResult($request)
    {
        $data = StudentResult::query()
            ->where('user_id', auth()->id())
            ->where('examination_id', $request->input('examination_id'))
            ->where('submit_type', 4)
            ->first();

        // 判断用户当前这场考试是否已经生成试卷 并且 未交卷
        if (!empty($data)) {
            return $data;
        }

        $request = Examination::query()
            ->select('id', 'test_list_id', 'subject_id', 'type')
            ->where('id', $request->input('examination_id'))
            ->first();

        // 获取试卷信息
        $testList = TestList::find($request['test_list_id']);

        // 获取试卷中的试题数据
        $testQuestion = testQuestion($testList);

        // 初始化用户试卷中的试题数据
        $content = [];
        foreach ($testQuestion as $key => $item) {
            $key += 1;
            $content[$key]['id'] = $item['id'];
            $content[$key]['content'] = $item['content'];
            $content[$key]['test_answer'] = $item['answer'];
            $content[$key]['answer'] = null;
            $content[$key]['fraction'] = $item['fraction'];
            $content[$key]['type'] = $item['type'];
            $content[$key]['is_true'] = null;
        }

        // 用户所属的部门
        $userSection = UserHasSection::query()
            ->select('section_id')
            ->where('user_id', auth()->id())
            ->get();

        // 获取id数组
        $ids = collect($userSection)->pluck('section_id');

        $userSection = ',';

        // 拼接字符串
        foreach ($ids as $v) {
            $userSection .= $v . ',';
        }

        $data = [];

        $user = auth()->user();

        // 初始化一张用户试卷所需要的数据
        $data['user_id'] = $user->id;
        $data['name'] = $user->name;
        $data['username'] = $user->username;
        $data['test_list_id'] = $request['test_list_id'];
        $data['examination_id'] = $request['id'];
        $data['type'] = $request['type'];
        $data['subject_id'] = $request['subject_id'];
        $data['fraction'] = 0;
        $data['success_num'] = 0;
        $data['already_num'] = 0;
        $data['all_num'] = count($testQuestion);
        $data['submit_type'] = 4;
        $data['user_section_ids'] = $userSection;
        $data['content'] = $content;

        $data = StudentResult::create($data);

        return $data;
    }
}

if (!function_exists('getExam')) {
    /**
     * 获取考场
     *
     * @return Builder|Model
     */
    function getExam($type, $examination_id = null)
    {
        return Examination::query()
            ->select(['id', 'name', 'passing_mark', 'subject_id', 'category_id', 'start_time', 'test_list_id', 'timer', 'over_time', 'show_result',
                'show_answer', 'mouse_out', 'mouse_out_num', 'status'])
            ->where([
                ['type', $type],
                ['status', 1],
                ['examination_mode', 0],
            ])
            ->orWhere([
                ['type', $type],
                ['status', 1],
                ['examination_mode', 1],
                ['peoples', 'like' , '%,' . auth()->id() . ',%']
            ])
            ->when($examination_id, function ($query) use ($examination_id) {
                $query->where('id', $examination_id);
            })
            ->with('subject:id,name')
            ->with('category:id,name')
            ->first();
    }
}


if (!function_exists('autoCorrect')) {
    /**
     * 自动阅卷
     *
     * @param $data
     * @return array
     */
    function autoCorrect($data)
    {

        $question = Question::query()
            ->select('type', 'success_answer')
            ->where('id', $data['question_id'])
            ->first();

        // 判断试题类型 调用指定批改方法
        if ($question->type == 'checkbox') {
            // 调用多选批改方式
            $res = checkbox($data, $question);
        } elseif ($question->type == 'fill') {
            // 调用填空批改方式
            $res = fill($data, $question);
        } elseif ($question->type == 'radio' || $question->type == 'judge') {
            // 通用通用批改方式
            $res = radioAndJudge($data, $question);
        } else {
            $res = [];
        }
        return $res;
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
 * 定义多选方法
 *
 * @param $userData
 * @param $question
 * @return array
 */
function checkbox($userData, $question)
{
    $res['id'] = $userData['question_id'];
    $res['answer'] = $userData['answer'];
    $res['type'] = $question['type'];
    $res['is_true'] = 0;
    $res['fraction'] = 0;

    // 多选题用户答案答案格式处理
    $data = explode(',', $userData['answer']);
    // 多选题正确答案答案格式处理
    $question = $question->success_answer;

    $question = trim(substr(trim(substr($question,0,strlen($question)-1)),1,strlen($question)-1));
    $question = explode(',', $question);
    // 正确答案数量
    $checkboxAllNum = count($question);

    // 用户多选的正确选项数量
    $checkboxNum = count(array_intersect($data, $question));

    if ($checkboxNum > $checkboxAllNum) {
        $res['is_true'] = 0;
        $res['fraction'] = 0;
    } else {
        if ($checkboxNum == $checkboxAllNum) {
            $res['is_true'] = 1;
            $res['fraction'] = $userData['fraction'];
        } elseif ($checkboxNum < $checkboxAllNum && $checkboxNum > 0) {
            $res['is_true'] = 2;
            $res['fraction'] = $userData['fraction'] / 2;
        } else {
            $res['is_true'] = 0;
            $res['fraction'] = 0;
        }
    }

    return $res;
}

/**
 * 定义填空批改方法
 *
 * @param $userData
 * @param $question
 * @return array
 */
function fill($userData, $question)
{
    $res['id'] = $userData['question_id'];
    $res['answer'] = $userData['answer'];
    $res['type'] = $question['type'];
    $res['is_true'] = 0;
    $res['fraction'] = 0;

    // 有序正确答案数组
    $successFillOls = rtrim(ltrim(
            substr($question['success_answer'],
                strpos($question['success_answer'], '('),
                strpos($question['success_answer'], ')')), '('), ')') ?? null;
    if (!empty($successFillOls)) {
        $successFillOls = explode(',', $successFillOls);
    }

    // 用户有序答案数组
    $fillOls =
        explode(',',
            rtrim(
                ltrim(
                    substr($userData['answer'],
                        strpos($userData['answer'], '('),
                        strpos($userData['answer'], ')')),
                    '('),
                ')')
        ) ?? null;

    // 无序正确答案数组
    $successFillUls = rtrim(ltrim(
            substr($question['success_answer'],
                strpos($question['success_answer'], '['),
                strpos($question['success_answer'], ']')), '['), ']') ?? null;
    if (!empty($successFillUls)) {
        $successFillUls = explode(',', $successFillUls);
    }

    // 用户无序答案数组
    $fillUls =
        explode(',',
            rtrim(
                ltrim(
                    substr($userData['answer'],
                        strpos($userData['answer'], '['),
                        strpos($userData['answer'], ']')),
                    '['),
                ']')
        ) ?? null;

    // 只有有序答案存在的情况
    if (!empty($successFillOls) && empty($successFillUls)) {
        // 填空正确数量
        $fillNum = 0;
        // 有序答案全部正确数量
        $fillAllNum = count($successFillOls);
        foreach ($fillOls as $key => $fillOl) {
            if ($successFillOls[$key] == $fillOl) {
                $fillNum += 1;
            }
        }
        // 全对
        if ($fillNum == $fillAllNum) {
            $res['fraction'] = $userData['fraction'];
            $res['is_true'] = 1;
            // 半对
        } elseif ($fillNum < $fillAllNum && $fillNum > 0) {
            $res['fraction'] = $userData['fraction'] / 2;
            $res['is_true'] = 2;
        } else {
            // 全错
            $res['fraction'] = 0;
            $res['is_true'] = 0;
        }
    }

    // 只有无序答案存在的情况
    if (empty($successFillOls) && !empty($successFillUls)) {
        // 填空正确数量
        $fillNum = 0;
        // 无序答案全部正确数量
        $fillAllNum = count($successFillUls);
        foreach ($fillUls as $fillUl) {
            if (in_array(trim($fillUl), $successFillUls)) {
                $fillNum += 1;
            }
        }
        // 全对
        if ($fillNum == $fillAllNum) {
            $res['fraction'] = $userData['fraction'];
            $res['is_true'] = 1;
            // 半对
        } elseif ($fillNum < $fillAllNum && $fillNum > 0) {
            $res['fraction'] = $userData['fraction'] / 2;
            $res['is_true'] = 2;
        } else {
            // 全错
            $res['fraction'] = 0;
            $res['is_true'] = 0;
        }
    }

    // 两种都存在的情况
    if (!empty($successFillOls) && !empty($successFillUls)) {
        // 无序答案前面的数字
        $str1 = substr($question['success_answer'], (strpos($question['success_answer'], '[') - 1), 1);
        // 顺序答案前面的数字
        $str2 = substr($question['success_answer'], (strpos($question['success_answer'], '(') - 1), 1);

        // 填空正确数量
        $fillNum = 0;
        // 有序答案全部正确数量
        $fillAllNum = intval($str1) + intval($str2);
        // 验证有序答案
        foreach ($fillOls as $key => $fillOl) {
            if ($successFillOls[$key] == $fillOl) {
                $fillNum += 1;
                $fillNum = $fillNum > $str2 ? $str2 : $fillNum;
            }
        }
        // 验证无序答案
        foreach ($fillUls as $fillUl) {
            if (in_array(trim($fillUl), $successFillUls)) {
                $fillNum += 1;
                $fillNum = $fillNum > $str1 ? $str1 : $fillNum;
            }
        }
        // 全对
        if ($fillNum == $fillAllNum) {
            $res['fraction'] = $userData['fraction'];
            $res['is_true'] = 1;
            // 半对
        } elseif ($fillNum < $fillAllNum && $fillNum > 0) {
            $res['fraction'] = $userData['fraction'] / 2;
            $res['is_true'] = 2;
        } else {
            // 全错
            $res['fraction'] = 0;
            $res['is_true'] = 0;
        }
    }
    return $res;
}

/**
 * 定义通用批改方法
 *2
 * @param $userData
 * @param $question
 * @return array
 */
function radioAndJudge($userData, $question)
{

    $res['id'] = $userData['question_id'];
    $res['answer'] = $userData['answer'];
    $res['type'] = $question['type'];
    $res['is_true'] = 0;
    $res['fraction'] = 0;


    // id相等 答案相等 正确 加分
    if ($userData['answer'] == $question['success_answer']) {
        $res['is_true'] = 1;
        $res['fraction'] = $userData['fraction'];
    } else {
        // id相等 答案不相等 错误 不加分
        $res['is_true'] = 0;
        $res['fraction'] = 0;
    }

    return $res;
}


// 更新题目正确次数
if (!function_exists('updateSuccessQuestion')) {
    function updateSuccessQuestion($id)
    {
        Question::query()
            ->where('id', $id)
            ->increment('success_num');
    }
}

// 更新题目错误次数
if (!function_exists('updateErrorQuestion')) {
    function updateErrorQuestion($id)
    {
        Question::query()
            ->where('id', $id)
            ->increment('error_num');
    }
}

// 获取登录用户的错题数量
if (!function_exists('userQuestionCount')) {
    function userQuestionCount($type = '')
    {
        return count(Redo::query()
            ->where('user_id', auth()->id())
            ->groupBy('type', 'id', 'user_id')
            ->having('type', '=', $type)
            ->pluck('type')
            ->toArray());
    }
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 * oss 配置
 */
if (!function_exists('getOssConfig')) {
    function getOssConfig()
    {
        if (Redis::exists('oss')) {
            $data = unserialize(Redis::get('oss'));
        } else {
            $data = Setting::query()
                ->where('type', 'oss')
                ->firstOrFail('value');

            $prefix = '';
            $accessKeyId = $data['value']['access_key'];
            $accessKeySecret = $data['value']['secret_key'];
            $endpoint = $data['value']['endpoint']; // ssl：https://iidestiny.com
            $bucket = $data['value']['bucket'];
            $isCName = $data['value']['is_name']; // 如果 isCname 为 false，endpoint 应配置 oss 提供的域名如：`oss-cn-beijing.aliyuncs.com`，cname 或 cdn 请自行到阿里 oss 后台配置并绑定 bucket

            $adapter = new OssAdapter($accessKeyId, $accessKeySecret, $endpoint, $bucket, $isCName, $prefix);

            $flysystem = new Filesystem($adapter);

            $oss_token = $flysystem->addPlugin(new SignatureConfig())
                ->signatureConfig($prefix = '/', $callBackUrl = '', $customData = [], $expire = 7200);
            Redis::setex('oss', 7200, serialize($oss_token));
        }

        return $data;
    }
}

/**
 * @param string 需要拼接的字段
 */
if (!function_exists('oss_url')) {
    function ossUrl($string = '')
    {
        $data = json_decode(getOssConfig(), true);
        return ($data['host'] ?? '') . $string;
    }
}
